/** ***********************************************************************
*  $Id: $
*
*  COPYRIGHT @2008 CSS CORP PVT LTD
*          
*  This program is free software; you can redistribute it and/or modify it under the terms of 
*  version 2 of the GNU General Public License as published by the Free Software 
*  Foundation.
*
*  The above copyright notice and this permission notice shall be included in all copies 
*  or substantial portions of the Software.
*
*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
*  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
*  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
*  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
*  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY
*  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
*  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
*  DEALINGS IN THE SOFTWARE.
*
************************************************************************* */

package com.css.ctg.tulip.service;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.MalformedURLException;
import java.util.Iterator;
import java.util.List;

import org.drools.RuleBase;
import org.drools.RuleBaseFactory;
import org.drools.WorkingMemory;
import org.drools.compiler.DroolsParserException;
import org.drools.compiler.PackageBuilder;
import org.drools.rule.Package;
import org.springframework.beans.factory.BeanFactory;
import org.xml.sax.SAXException;

import com.css.ctg.tulip.bean.TulipDataBean;
import com.css.ctg.tulip.exception.TulipException;
import com.css.ctg.tulip.info.TulipBrokerInfo;
import com.css.ctg.tulip.info.TulipResultInfo;
import com.css.ctg.tulip.jaxb.config.Rulegroup;
import com.css.ctg.tulip.jaxb.config.TulipService;
import com.css.ctg.tulip.util.TulipLogger;
import com.gargoylesoftware.htmlunit.ThreadedRefreshHandler;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.meterware.httpunit.HttpNotFoundException;
import com.meterware.httpunit.HttpUnitOptions;
import com.meterware.httpunit.WebApplet;
import com.meterware.httpunit.WebConversation;
import com.meterware.httpunit.WebImage;
import com.meterware.httpunit.WebLink;
import com.meterware.httpunit.WebResponse;

/**
 * <p>	Framework abstract action class. This class to be extended by the user.
 * 
 * <p>	This class is used to verify the content of the URL specified by the user
 * 		with the help of HttpUnit API, Rule Engine. And produce the output which is
 * 		used to generate result XML.
 * 
 * @version $Rev:
 * 			$
 * 
 */

public abstract class TulipAbstractAction {
	
	private String className = "TulipAbstractAction";
	
	
	/**
	 * <p>	Used to create a instance for the TulipDataBean(with the help of user
	 * 		Spring config xml file). Sets the data for DataBean using HttpUnit API.
	 * 		Calls the abstract execute method of this class which user Override.
	 * 		Fires all the rules, which set the values for the TulipResultInfo.
	 * 
	 * @param tulipService
	 * @param path
	 * @param info
	 * @return TulipResultINfo
	 * @throws TulipException
	 */
	public TulipResultInfo execute(TulipService tulipService, 
			BeanFactory beanFactory, TulipBrokerInfo info) throws TulipException {

		// Logging action
		TulipLogger.log(className, TulipLogger.info, "Inside execute(TulipService tulipService, BeanFactory beanFactory ,TulipBrokerInfo info) of TulipAbstractAction");
		
		// Method attributes
		TulipResultInfo resultInfoImpl = new TulipResultInfo();
		TulipDataBean dataBean = null;
		

		// Instantiate the bean.
		dataBean = (TulipDataBean) beanFactory.getBean(tulipService.getDatabean());		

		// Logging action
		TulipLogger.log(className, TulipLogger.info, "Preparing data and calling execute method");
		
		// calling prepareDate method to set the value for the TulipDataBean.
		dataBean = prepareData(info.getUrl(), dataBean);

		// calling execute method to set the value for the user bean.
		dataBean = execute(info.getUrl(), dataBean);

		try {
			// Logging action
			TulipLogger.log(className, TulipLogger.info, "Calling readRule(drl file name)");
			
			List<Rulegroup> ruleGroup = tulipService.getRulegroup();

			for (int ruleGroupIndex = 0; ruleGroupIndex < ruleGroup.size(); ruleGroupIndex++) {
				// load up the rulebase
				TulipLogger.log(className, TulipLogger.debug, ruleGroup.get(ruleGroupIndex).getDrlfile());
				
				RuleBase ruleBase = readRule(ruleGroup.get(ruleGroupIndex).getDrlfile());
				WorkingMemory workingMemory = ruleBase.newWorkingMemory();

				// Set the global variable for storing the Result.
				workingMemory.setGlobal(globalVarName(), resultInfoImpl);

				// assert object which contains the details to verify by the rules.
				workingMemory.assertObject(dataBean);

				// fire all the rules.
				workingMemory.fireAllRules();
			}

		} catch (Exception e) {
			throw new TulipException(className, e);
		}		

		// return the result which contains the output of rules
		return resultInfoImpl;
	}

	/**
	 * <p>	User Overrides this method to set the values needed for user dataBean.
	 * 
	 * @param url - String
	 * @param dataBean - TulipDataBean
	 * @return TulipDataBean
	 * @throws TulipException
	 */
	protected abstract TulipDataBean execute(String url, TulipDataBean dataBean) throws TulipException;

	/**
	 * <p>	Used to specify the name of the global variable which is declared in the .drl file.
	 * 
	 * @return String
	 */
	protected abstract String globalVarName();

	/**
	 * <p>	Used to specify the USER_AGENT header information for the url.
	 * 
	 * @param webConverstation - Web Conversation 
	 * @return WebConverstaion
	 * @throws TulipException
	 * 
	 */
	protected abstract WebConversation beforePrepareData(WebConversation webConversation) throws TulipException;

	/**
	 * <p>	Used For additional info.
	 * 
	 * @return void
	 * @throws TulipException
	 */
	protected abstract void afterPrepareData() throws TulipException;

	/**
	 * <p>	Read the drool file, compile it and add to RuleBase.
	 * 
	 * @param drlFileName - String
	 * @return RuleBase
	 * @throws TulipException
	 */
	protected RuleBase readRule(String drlFileName) throws TulipException {
		
		// Logging action
		TulipLogger.log(className, TulipLogger.info, "Inside readRule(String drlFileName) of TulipAbstractAction");
		
		// Declaration and Initialize method variables
		Reader source = null;
		PackageBuilder builder = null;
		Package pkg = null;
		RuleBase ruleBase = null;
		
		try {

			// read in the source
			source = new InputStreamReader(TulipAbstractAction.class.getResourceAsStream(drlFileName));
	
			// New instance
			builder = new PackageBuilder();
			builder.addPackageFromDrl(source);
	
			// get the compiled package (which is serializable)
			pkg = builder.getPackage();
	
			// add the package to a rulebase (deploy the rule package).
			ruleBase = RuleBaseFactory.newRuleBase();
			ruleBase.addPackage(pkg);
		} catch (DroolsParserException dpe) {
			throw new TulipException(className, dpe);
		} catch (IOException ioe) {
			throw new TulipException(className, ioe);
		} catch (Exception e) {
			throw new TulipException(className, e);
		}

		return ruleBase;
	}

	/**
	 * <p>	Set the values for the TulipDataBean.
	 * 
	 * @param url - String
	 * @param dataBean - TulipDataBean
	 * @return TulipDataBean
	 * @throws TulipException
	 */
	private TulipDataBean prepareData(String url, TulipDataBean dataBean) throws TulipException {

		// Logging action
		TulipLogger.log(className, TulipLogger.info, "Inside prepareData(String url,TulipDataBean dataBean) of TulipAbstractAction");
		
		// Declare and Initialize
		WebConversation wc = null;
		WebResponse response = null;
		
		try {
			HttpUnitOptions.setScriptingEnabled(false);
			
			// Create new instance
			wc = new WebConversation();
			
			wc = beforePrepareData(wc);			
			response = wc.getResponse(url);

			// To retrieve the details of content type
			if (response.getContentType().length() > -1)
				dataBean.setContentType(response.getContentType());

			if (response.getContentType().indexOf("html") > -1) {
				// To retrieve image details.
				WebImage[] imageArray = response.getImages();
				dataBean.setImage(imageArray.length > 0 ? true : false);
				
				// To retrieve Applets details.
				WebApplet[] appletArray = response.getApplets();
				dataBean.setApplet(appletArray.length > 0 ? true : false);

				// To retrieve frame details
				String[] frameArray = response.getFrameNames();
				dataBean.setFrame(frameArray.length > 0 ? true : false);

				// To retrieve details of the links associated with this web site.
				WebLink[] webLinks = response.getLinks();
				if (webLinks.length > 0) {
					String[] webLinkArray = new String[webLinks.length];
					for (int linkIndex = 0; linkIndex < webLinks.length; linkIndex++) {
						webLinkArray[linkIndex] = webLinks[linkIndex].getURLString();
					}
					
					dataBean.setWebLinkArrayLength(webLinkArray.length);
					
					for (int i = 0; i < webLinkArray.length; i++) {
						dataBean.setWebLinkArray(i, webLinkArray[i]);
					}
				}

				// To retrieve details of the embedded files, file upload and mouseOver.
				dataBean = otherContent(url, dataBean);
			}
		} catch (MalformedURLException malurle) {
			throw new TulipException(className, malurle);
		} catch (IOException ioe) {
			throw new TulipException(className, ioe);
		} catch (SAXException saxe) {
			throw new TulipException(className, saxe);
		} catch (HttpNotFoundException httpe) {
			throw new TulipException(className, httpe);
		} /*catch (RuntimeException rune) {
			throw new TulipException(className, rune);
		}*/ catch (Exception e) {
			throw new TulipException(className, e);
		}
		return dataBean;
	}

	/**
	 * 
	 * <p>	Set the values for the TulipDataBean for embedded SVG, embedded SWF, file
	 * 		upload and MouseOver.
	 * 
	 * @param url - String
	 * @param dataBean - TulipDataBean
	 * @return TulipDataBean
	 * @throws TulipException
	 */
	private TulipDataBean otherContent(String url, TulipDataBean dataBean) throws TulipException {

		WebClient webClient = new WebClient();
		int noOfEmbeddedSwfFiles = 0;
		int noOfEmbeddedSvgFiles = 0;
		int noOfFileUpload = 0;
		int noOfMouseOver = 0;

		try {
			webClient.setRefreshHandler(new ThreadedRefreshHandler());

			HtmlPage page = (HtmlPage) webClient.getPage(url);

			Iterator<?> node = page.getAllHtmlChildElements();
			String nextNode = null;
			String value = null;
			while (node.hasNext()) {
				nextNode = node.next().toString();
				// Details of Embed file
				if (nextNode.indexOf("UnknownHtmlElement") != -1) {
					value = nextNode;
					if ((value != null) && value.indexOf(".swf") != -1) {
						noOfEmbeddedSwfFiles++;
					}
					if ((value != null) && value.indexOf(".svg") != -1
							|| (value != null) && value.indexOf("<svg") != -1) {
						noOfEmbeddedSvgFiles++;
					}
				}

				// File Upload Details
				if (nextNode.indexOf("HtmlFileInput") != -1) {
					noOfFileUpload++;
				}
				if (nextNode.indexOf("onmouseover") != -1) {
					noOfMouseOver++;

				}
			}
			dataBean.setNoOfEmbeddedSvgFiles(noOfEmbeddedSvgFiles);
			dataBean.setNoOfEmbeddedSwfFiles(noOfEmbeddedSwfFiles);
			dataBean.setNoOfFileUpload(noOfFileUpload);
			dataBean.setNoOfMouseOver(noOfMouseOver);
		} catch (Exception e) {
			throw new TulipException(className, e);
		}
		return dataBean;
	}
}
